package edu.kit.ksri.db.mturk.workerpools;

import java.io.Serializable;

import org.apache.log4j.Logger;

import edu.kit.ksri.db.mturk.connectionhandlers.AbstractHITManager;
import edu.kit.ksri.db.mturk.var.StaticInformation;

/**
 * Copyright (c) 2010-2011 Karlsruhe Institute of Technology (KIT), Germany
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 * 
 * --------------------------------------------------------------------------
 * 
 * Software: CSP/WMV tool for dynamic quality management of microtasks
 * http://www.ksri.kit.edu/Upload/Users/PeopleClouds/HCOMP2011_An_extendable_toolkit_for_managing_quality_of_human-based_eServices.pdf
 * 
 * Organization: Karlsruhe Institute of Technology (KIT), Germany
 * http://www.kit.edu
 * 
 * Project: PeopleClouds project of the Karlsruhe Service Research Institute(KSRI)
 * http://www.ksri.kit.edu/Default.aspx?PageId=818
 * 
 * Contributing authors: David Bermbach, Sandra Rath, Pascal Wichmann, Robert Kern
 * 
 * ---------------------------------------------------------------------------
 * 
 * Purpose: Represents a pServices Worker. 
 * Note: this class has a natural ordering that is inconsistent with equals
 */
public class StandardWorker extends AbstractWorker implements Serializable, CSP1Capable{

	private static final long serialVersionUID = -6293585872684730422L;

	private static final Logger basiclog = Logger
	.getLogger("basic." + AbstractHITManager.class);	/**
	
	 * determines whether the next assignment needs to be inspected due to CSP1
	 */
	private boolean mustBeInspectedForCSP1 = true;

	/**
	 * specifies how many units have been inspected in a row and have been found
	 * to be correct. If this value exceeds {@link StaticInformation}.i only
	 * fractions are inspected.
	 */
	private int inspectedCorrectSamplesInARow = 0;

	/**
	 * the total number of assignments of this worker which were found to be
	 * correct based on inspection
	 */
	private int totalInspectedCorrect = 0;

	/**
	 * the total number of assignments of this worker which were found to be
	 * incorrect based on inspection
	 */
	private int totalInspectedFalse = 0;

	/**
	 * defines whether every unit or only the fraction {@link StaticInformation}
	 * .f is inspected
	 */
	// ROB_APRIL09 >>
	//private InspectionMode inspectionMode = InspectionMode.FULL;
	// ROB_APRIL09 <<
	
	/**
	 * @param workerId
	 */
	public StandardWorker(String workerId) {
		super();
		this.workerId = workerId;
	}

	/**
	 * @param workerId
	 * @param totalAssignments
	 * @param totalInspectedCorrect
	 * @param totalInspectedFalse
	 */
	public StandardWorker(String workerId, int totalAssignments,
			int totalInspectedCorrect, int totalInspectedFalse) {
		super();
		this.workerId = workerId;
		this.totalAssignments = totalAssignments;
		this.totalInspectedCorrect = totalInspectedCorrect;
		this.totalInspectedFalse = totalInspectedFalse;
		this.correctnessLevel = 100
		// Only inspected samples may be taken into account!
		* this.totalInspectedCorrect / (this.totalInspectedCorrect + this.totalInspectedFalse);
	}

	@Override
	public boolean equals(Object arg) {
		// System.out.println("EQUALS invoked for " + workerId + " and "
		// + ((Worker) arg).workerId + ", returning: "
		// + (this.workerId.equals(((Worker) arg).workerId)));
		if (!(arg instanceof StandardWorker))
			return false;
		return this.workerId.equals(((StandardWorker) arg).workerId);
	}

	/**
	 * 
	 * @return a String [] version of all the fields of this worker. Is used for
	 *         CSV export of worker pools.
	 */
	public synchronized String[] toStringArray() {
		String[] res = new String[3];
		res[0] = "" + workerId;
		res[1] = "" + correctnessLevel;
		res[2] = "" + mustBeInspectedForCSP1;
		return res;
	}

	public int compareTo(AbstractWorker other) {
		if (!(other instanceof StandardWorker))
			try {
				throw new Exception("Cannot compare workers of different types.");
			} catch (Exception e) {
				e.printStackTrace();
			}
		if (this.correctnessLevel < other.correctnessLevel)
			return 1;
		if (this.correctnessLevel > other.correctnessLevel)
			return -1;
		return 0;
	}

	/**
	 * updates this worker's statistics based on CSP-1. based on the parameters
	 * it updates all counter fields, determines whether the inspection mode
	 * needs to change and finally updates the field mustBeInspectedForCSP1.
	 * 
	 * @param wasInspected
	 *            whether the assignment was inspected or not
	 * @param wasCorrect
	 *            whether the inspection resulted in pass (true) or fail
	 *            (false). if wasInspected == false this parameter is ignored.
	 */
	public void addAssignmentForBinomialCSP1(boolean wasInspected,
			boolean wasCorrect) {
		synchronized (syncLockField) {
			this.totalAssignments++;
			if (wasInspected) {
				// If the worker was not inspected, nothing needs to be done!
				if (wasCorrect) {
					this.totalInspectedCorrect++;
					this.inspectedCorrectSamplesInARow++;
				} else {
					this.totalInspectedFalse++;
					this.inspectedCorrectSamplesInARow = 0;
				}
			}
			// Update correctness level
			this.correctnessLevel = 100
				// Only inspected samples may be taken into account!
				* this.totalInspectedCorrect / (this.totalInspectedCorrect + this.totalInspectedFalse);

			// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
			// April 9, 2011: temporary fix for phi=1 problem: avoid that phi is ever getting
			// exactly 1
			// this.correctnessLevel = this.correctnessLevel * 0.99999999;
			// July 5, 2011: removed temporary fix. Phi = 1 case should be avoided by preventing
			// 100% correct qualification test results in QualificationManager.java! 
			// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

			// Update mustBeInspectedForCSP1 field
			if ( this.inspectedCorrectSamplesInARow >= StaticInformation.scenario.getI() ) {
				// If the number i of inspected correct assignments is met or exceeded
				// decide randomly whether to inspect or not.
				// (Math.random() provides random numbers between 0 and 1)
				this.mustBeInspectedForCSP1 =
					(Math.random() <= StaticInformation.scenario.getF());
			} else {
				// In i is not met, the following assignment has to be inspected
				this.mustBeInspectedForCSP1 = true;
			}
			basiclog.info("Worker " + this.workerId + " has returned " +
					this.inspectedCorrectSamplesInARow + " correct results in a row."); 
			basiclog.info("Inspection of worker required? " + this.mustBeInspectedForCSP1);
		}
	}

	/**
	 * changes the inspection mode and applies the necessary updates to the
	 * fields inspectionMode, totalAssignmentsInFractionalMode,
	 * totalAssignmentsInspectedInFractionalMode and
	 * inspectedCorrectSamplesInARow (initializes all counters)
	 */
	
	/**
	 * @return whether this worker's next assignment must be inspected or not
	 */
	public boolean isMustBeInspectedForCSP1() {
		return this.mustBeInspectedForCSP1;
	}

	/**
	 * @return the totalInspectedCorrect
	 */
	public int getTotalInspectedCorrect() {
		return this.totalInspectedCorrect;
	}

	/**
	 * @return the totalInspectedFalse
	 */
	public int getTotalInspectedFalse() {
		return this.totalInspectedFalse;
	}
	
}
